package com.donger.common.log.annotation;

import cn.hutool.core.exceptions.ExceptionUtil;
import com.donger.common.core.event.SysLogEvent;
import com.donger.common.core.utils.HttpContextUtil;
import com.donger.common.core.utils.SpringContextUtil;
import com.donger.common.security.utils.SecurityUtil;
import com.donger.upms.api.entity.SysLog;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * 日志AOP
 *
 * @author xyx
 * @date 2019-01-10 21:03
 */
@Slf4j
@Aspect
@Component
public class LogAop {

    @Pointcut(value = "@annotation(com.donger.common.log.annotation.Log)")
    public void cutService() {
    }

    @Around("cutService()")
    public Object recordSysLog(ProceedingJoinPoint point) throws Throwable {

        long startTime = System.currentTimeMillis();
        //先执行业务
        Object result = point.proceed();
        long endTime = System.currentTimeMillis();
        try {
            handle(point, endTime - startTime, null);
        } catch (Exception e) {
            log.error("日志记录出错!", e);
        }
        return result;
    }

    @AfterThrowing(pointcut = "cutService()", throwing = "e")
    public void doAfterThrowing(JoinPoint point, Throwable e) {
        try {
            handle(point, null, e);
        } catch (Exception ex) {
            log.error("日志记录出错!", ex);
        }
    }

    private void handle(JoinPoint point, Long methodProcessTime, Throwable e) throws Exception {
        //获取拦截的方法名
        Signature sig = point.getSignature();
        MethodSignature msig = null;
        if (!(sig instanceof MethodSignature)) {
            throw new IllegalArgumentException("该注解只能用于方法");
        }
        msig = (MethodSignature) sig;
        Object target = point.getTarget();
        Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
        String methodName = currentMethod.getName();
        //获取拦截方法的参数
        Object[] params = point.getArgs();
        //获取操作名称
        Log annotation = currentMethod.getAnnotation(Log.class);
        String moduleName = annotation.moduleName();
        String operationName = annotation.value();
        SysLog sysLog = new SysLog();
        sysLog.setModuleName(moduleName);
        sysLog.setOperation(operationName);
        sysLog.setMethod(methodName);
        sysLog.setTime(methodProcessTime);
        sysLog.setRequestUrl(HttpContextUtil.requestUrl());
        sysLog.setRequestMethod(HttpContextUtil.requestMethod());
        sysLog.setUserAgent(HttpContextUtil.getUserAgent());
        sysLog.setIp(HttpContextUtil.getRemoteAddr());
        sysLog.setParams(Arrays.toString(params));
        sysLog.setLogType(SysLog.ACCESS_LOG);
        SecurityUtil.getCurrentUserLogin().ifPresent(userDetail -> {
            sysLog.setCreateBy(userDetail.getUserId());
            sysLog.setUsername(userDetail.getUsername());
        });
        if (e != null) {
            sysLog.setLogType(SysLog.EXCEPTION_LOG);
            sysLog.setException(ExceptionUtil.getMessage(e));
        }
        SpringContextUtil.publishEvent(new SysLogEvent(sysLog));
    }

}
